﻿using EasyCaching.Core;
using HDS.Common.Helper;
using HDS.Data;
using HDS.IService.Master;
using HDS.Model;
using HDS.Model.Entity.Master;
using HDS.Model.Entity.View;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TimeZoneConverter;

namespace HDS.Service.Master
{
    public class DataCacheService : IDataCacheService
    {
        private MasterDbContext dbContext = new MasterDbContext();
        private IEasyCachingProvider _IEasyCachingProvider;

        public DataCacheService(IEasyCachingProvider cachingProvider)
        {
            _IEasyCachingProvider = cachingProvider;
        }

        public async Task<DistributorConfig> GetDistributorConfig(string distributor)
        {
            if (distributor.Equals(GlobalVariable.DISTRIBUTOR_DEFAULT))
                return new DistributorConfig
                {
                    Distributor = distributor,
                    DistributorGroup = distributor,
                    CommissionRate = 0,
                    ConfigData = "{}",
                    Isonline = true,
                    IsSellingPrice = 1,
                    Remark = "",
                    PlusCancelHour = 24
                };

            List<DistributorConfig> list;
            var cacheKey = $"distributors";
            var res = await _IEasyCachingProvider.GetAsync<List<DistributorConfig>>(cacheKey);
            if (res.HasValue)
            {
                list = res.Value;
            }
            else
            {
                list = await dbContext.DistributorConfig.GetListAsync();
                await _IEasyCachingProvider.SetAsync<List<DistributorConfig>>(cacheKey, list, TimeSpan.FromMinutes(10));
            }

            var config = list.Where(e => e.Distributor.Equals(distributor)).FirstOrDefault();
            if (config == null)
                return new DistributorConfig
                {
                    Distributor = distributor,
                    DistributorGroup = distributor,
                    CommissionRate = 0,
                    ConfigData = "{}",
                    Isonline = false,
                    IsSellingPrice = 1,
                    Remark = ""
                };

            return config;
        }

        public async Task<VDistributorHotel> GetHotel(string distributor, int hotelId)
        {
            if (distributor.Equals(GlobalVariable.DISTRIBUTOR_DEFAULT))
            {
                var hotel = await dbContext.Hotel.GetFirstAsync(e => e.HotelId.Equals(hotelId));
                if (hotel == null)
                    return null;

                return new VDistributorHotel {
                    HotelId = hotel.HotelId,
                    HotelNameEn = hotel.HotelNameEn,
                    HotelNameCn = hotel.HotelNameCn,
                    HotelStar = hotel.HotelStar,
                    CountryCode = hotel.CountryCode,
                    CountryName = hotel.CountryName,
                    ProvinceCode = hotel.ProvinceCode,
                    ProvinceName = hotel.ProvinceName,
                    CityCode = hotel.CityCode,
                    CityName = hotel.CityName,
                    ZoneCode = hotel.ZoneCode,
                    ZoneName = hotel.ZoneName,
                    Postalcode = hotel.Postalcode,
                    HotelAddressEn = hotel.HotelAddressEn,
                    HotelAddressCn = hotel.HotelAddressCn,
                    HotelLongitude = hotel.HotelLongitude,
                    HotelLatitude = hotel.HotelLatitude,
                    HotelTel = hotel.HotelTel,
                    HotelFax = hotel.HotelFax,
                    HotelEmail = hotel.HotelEmail,
                    HotelWeb = hotel.HotelWeb,
                    HotelStatus = hotel.HotelStatus,
                    HotelMd5New = hotel.HotelMd5,
                    Id = 0,
                    Distributor = distributor,
                    DistributorHotelId = "",
                    DistributorHotelId1 = "",
                    DistributorCityId = "",
                    DistributorCityName = "",
                    DistributorWeight = 0,
                    HotelMd5 = hotel.HotelMd5,
                    Isonline = true,
                    PushStatus = PushStatus.NotPush,
                    PushError = "",
                    PushResult = PushResult.NotPush,
                    UpdatedBy = hotel.UpdatedBy,
                    UpdatedByName = hotel.UpdatedByName,
                    UpdatedTime = hotel.UpdatedTime,
                };
            }

            var cacheKey = $"{hotelId}_distributor";
            var res = await _IEasyCachingProvider.GetAsync<VDistributorHotel>(cacheKey);
            if (res.HasValue)
            {
                return res.Value;
            }
            else
            {
                var hotelInfo = await dbContext.Db.SqlQueryable<VDistributorHotel>(DistributorService.VDistributorHotelSQL).Where(e => e.Distributor.Equals(distributor) && e.HotelId.Equals(hotelId)).FirstAsync();
                await _IEasyCachingProvider.SetAsync<VDistributorHotel>(cacheKey, hotelInfo, TimeSpan.FromHours(2));
                return hotelInfo;
            }
        }

        public async Task<List<DistributorSupplierSetting>> GetSupplierSettings(string distributor, int hotelId, DateTime checkinDate, DateTime checkoutDate, string countryCode, string cityCode)
        {
            var stayNights = DateTimeHelper.GetNights(checkinDate, checkoutDate);
            var hotelLocalNow = await GetLocalNowTime(countryCode, cityCode); ///酒店当地时间
            var spaceNights = DateTimeHelper.GetNights(hotelLocalNow, checkinDate);

            var settingList = await GetSupplierSettings(distributor);
            var hotelSettingList = await GetHotelSettings(distributor, hotelId);
            var mapHotelList = await GetHotelMappings(hotelId);

            foreach (var setting in settingList)
            {
                var mapHotel = mapHotelList.Where(e => e.Supplier.Equals(setting.Supplier)).FirstOrDefault();
                if (mapHotel == null) //无匹配
                {
                    setting.Isonline = false;
                    continue;
                }

                setting.SupplierHotelId = mapHotel.SupplierHotelId;

                var hotelSetting = hotelSettingList.Where(e => e.Supplier.Equals(setting.Supplier)).FirstOrDefault();
                if (hotelSetting != null)
                {
                    if (hotelSetting.Isonline.HasValue)
                        setting.Isonline = hotelSetting.Isonline.Value;
                    if (hotelSetting.MinAdvBookHour.HasValue)
                        setting.MinAdvBookHour = hotelSetting.MinAdvBookHour.Value;
                    if (hotelSetting.MinStayNights.HasValue)
                        setting.MinStayNights = hotelSetting.MinStayNights.Value;
                    if (hotelSetting.InvalidStartTime.HasValue)
                        setting.InvalidStartTime = hotelSetting.InvalidStartTime.Value;
                    if (hotelSetting.InvalidEndTime.HasValue)
                        setting.InvalidEndTime = hotelSetting.InvalidEndTime.Value;
                    if (hotelSetting.UseCache.HasValue)
                        setting.UseCache = hotelSetting.UseCache.Value;
                }


                if (setting.LimitNights < spaceNights) //限制晚数
                {
                    setting.Isonline = false;
                    continue;
                }

                if (stayNights < setting.MinStayNights) //最小入住晚数
                {
                    setting.Isonline = false;
                    continue;
                }

                if (setting.InvalidStartTime.HasValue && setting.InvalidEndTime.HasValue) //每日禁售时间
                {
                    if (hotelLocalNow.Hour >= setting.InvalidStartTime.Value && hotelLocalNow.Hour < setting.InvalidEndTime.Value)
                    {
                        setting.Isonline = false;
                        continue;
                    }
                }

                if (setting.MinAdvBookHour > (checkinDate.AddDays(1) - hotelLocalNow).TotalHours) //最小提前预订小时数
                {
                    setting.Isonline = false;
                    continue;
                }
            }

            return settingList.Where(e => e.Isonline && !string.IsNullOrEmpty(e.SupplierHotelId)).ToList();
        }

        public async Task<List<SupplierApiConfig>> GetSupplierApiConfigs(string[] suppliers)
        {
            List<SupplierApiConfig> list;
            var cacheKey = "suppliers";
            var res = await _IEasyCachingProvider.GetAsync<List<SupplierApiConfig>>(cacheKey);
            if (res.HasValue)
            {
                list = res.Value;
            }
            else
            {
                list = await dbContext.SupplierApiConfig.GetListAsync();
                await _IEasyCachingProvider.SetAsync<List<SupplierApiConfig>>(cacheKey, list, TimeSpan.FromHours(24));
            }

            if (!suppliers.IsEmpty())
                return list.Where(e => suppliers.Contains(e.Supplier)).ToList();
            else
                return list;
        }

        public async Task<List<HotelCommission>> GetHotelCommissions(string distributor, int hotelId, DateTime checkinDate, DateTime checkoutDate, string countryCode, string cityCode)
        {
            var hotelLocalNow = await GetLocalNowTime(countryCode, cityCode); ///酒店当地时间

            List<HotelCommission> list;
            var cacheKey = $"{distributor}_commission";
            var res = await _IEasyCachingProvider.GetAsync<List<HotelCommission>>(cacheKey);
            if (res.HasValue)
            {
                list = res.Value;
            }
            else
            {
                list = await dbContext.HotelCommission.GetListAsync(e => e.Distributor.Equals(distributor));
                await _IEasyCachingProvider.SetAsync<List<HotelCommission>>(cacheKey, list, TimeSpan.FromHours(1));
            }

            var resultList = new List<HotelCommission>();
            foreach (var commission in list)
            {
                if (commission.StartDate.HasValue && commission.EndDate.HasValue)
                {
                    if (checkinDate < commission.StartDate.Value || checkinDate >= commission.EndDate.Value)
                    {
                        continue;
                    }
                }
                else //当天预订
                {
                    if (checkinDate.Date != hotelLocalNow.Date)
                    {
                        continue;
                    }
                }

                if (commission.StartTime.HasValue && commission.EndTime.HasValue)
                {
                    if (hotelLocalNow.Hour < commission.StartTime.Value || hotelLocalNow.Hour >= commission.EndTime.Value)
                    {
                        continue;
                    }
                }

                if (!string.IsNullOrEmpty(commission.CountryCode) && !commission.CountryCode.Equals(countryCode))
                {
                    continue;
                }

                if (!string.IsNullOrEmpty(commission.CityCode) && !commission.CityCode.Equals(cityCode))
                {
                    continue;
                }

                resultList.Add(commission);
            }

            return resultList;
        }

        public async Task<List<VMappingRoom>> GetRoomMappings(int hotelId)
        {
            var cacheKey = $"{hotelId}_room_mappings";
            var res = await _IEasyCachingProvider.GetAsync<List<VMappingRoom>>(cacheKey);
            if (res.HasValue)
            {
                return res.Value;
            }
            else
            {
                var sql = " SELECT";
                sql += " r.`room_id`,";
                sql += " r.`hotel_id`,";
                sql += " r.`room_name_en`,";
                sql += " r.`room_name_cn`,";
                sql += " r.`room_area`,";
                sql += " m.`supplier`,";
                sql += " m.`supplier_hotel_id`,";
                sql += " m.`supplier_room_id`,";
                sql += " m.`mapping_status`,";
                sql += " m.`updated_by`,";
                sql += " m.`updated_by_name`,";
                sql += " m.`updated_time`,";
                sql += " sr.`room_name_en` supplier_room_name_en,";
                sql += " sr.`room_name_cn` supplier_room_name_cn,";
                sql += " sr.`room_desc`";
                sql += " FROM `hotel_room` r";
                sql += " LEFT JOIN mapping_room m ON m.`hotel_id` = r.`hotel_id` AND m.`room_id` = r.`room_id`";
                sql += " LEFT JOIN supplier_room sr ON sr.`supplier` = m.`supplier` AND sr.`supplier_hotel_id` = m.`supplier_hotel_id` AND sr.`supplier_room_id` = m.`supplier_room_id`";
                sql += $" WHERE r.`room_status` = 1 AND m.mapping_status = 1 AND sr.room_status = 1 AND r.hotel_id={hotelId}";
                var list = await dbContext.Db.SqlQueryable<VMappingRoom>(sql).ToListAsync();
                await _IEasyCachingProvider.SetAsync<List<VMappingRoom>>(cacheKey, list, TimeSpan.FromHours(2));
                return list;
            }
        }

        public async Task<List<ExchangeRate>> GetExchangeRates()
        {
            var cacheKey = "exchange_rates";
            var res = await _IEasyCachingProvider.GetAsync<List<ExchangeRate>>(cacheKey);
            if (res.HasValue)
            {
                return res.Value;
            }
            else
            {
                var list = await dbContext.ExchangeRate.GetListAsync();
                await _IEasyCachingProvider.SetAsync<List<ExchangeRate>>(cacheKey, list, TimeSpan.FromHours(8));
                return list;
            }
        }

        public async Task RemoveCache(string cacheKey)
        {
            await _IEasyCachingProvider.RemoveAsync(cacheKey);
            await _IEasyCachingProvider.RemoveByPrefixAsync(cacheKey);
        }




        protected async Task<List<DistributorSupplierSetting>> GetSupplierSettings(string distributor)
        {
            var cacheKey = $"{distributor}_supplier_setting";
            var res = await _IEasyCachingProvider.GetAsync<List<DistributorSupplierSetting>>(cacheKey);
            if (res.HasValue)
            {
                return res.Value;
            }
            else
            {
                var list = await dbContext.DistributorSupplierSetting.GetListAsync(e => e.Distributor.Equals(distributor) && e.Isonline);
                await _IEasyCachingProvider.SetAsync<List<DistributorSupplierSetting>>(cacheKey, list, TimeSpan.FromHours(2));
                return list;
            }
        }
        protected async Task<List<DistributorHotelSetting>> GetHotelSettings(string distributor, int hotelId)
        {
            var cacheKey = $"{hotelId}_hotel_setting_{distributor}";
            var res = await _IEasyCachingProvider.GetAsync<List<DistributorHotelSetting>>(cacheKey);
            if (res.HasValue)
            {
                return res.Value;
            }
            else
            {
                var list = await dbContext.DistributorHotelSetting.GetListAsync(e => e.Distributor.Equals(distributor) && e.HotelId.Equals(hotelId));
                await _IEasyCachingProvider.SetAsync<List<DistributorHotelSetting>>(cacheKey, list, TimeSpan.FromHours(2));
                return list;
            }
        }
        protected async Task<List<MappingHotel>> GetHotelMappings(int hotelId)
        {
            var cacheKey = $"{hotelId}_hotel_mapping";
            var res = await _IEasyCachingProvider.GetAsync<List<MappingHotel>>(cacheKey);
            if (res.HasValue)
            {
                return res.Value;
            }
            else
            {
                var list = await dbContext.MappingHotel.GetListAsync(e => e.HotelId.Equals(hotelId) && e.MappingStatus);
                await _IEasyCachingProvider.SetAsync<List<MappingHotel>>(cacheKey, list, TimeSpan.FromHours(2));
                return list;
            }
        }



        protected async Task<DateTime> GetLocalNowTime(string countryCode, string cityCode)
        {
            if (string.IsNullOrEmpty(countryCode))
                return DateTime.Now;

            var cityList = await GetCities();
            var city = cityList.Where(e => e.CityCode.Equals(cityCode)).FirstOrDefault();
            if (city != null && !string.IsNullOrEmpty(city.TimezoneId))
                return TimeZoneInfo.ConvertTime(DateTime.Now, TZConvert.GetTimeZoneInfo(city.TimezoneId));

            var countryList = await GetCountries();
            var country = countryList.Where(e => e.CountryCode.Equals(countryCode)).FirstOrDefault();
            if (country == null || string.IsNullOrEmpty(country.TimezoneId))
                return DateTime.Now;

            return TimeZoneInfo.ConvertTime(DateTime.Now, TZConvert.GetTimeZoneInfo(country.TimezoneId));
        }

        protected async Task<List<City>> GetCities()
        {
            var cacheKey = "timezone_cities";
            var res = await _IEasyCachingProvider.GetAsync<List<City>>(cacheKey);
            if (res.HasValue)
            {
                return res.Value;
            }
            else
            {
                var list = await dbContext.Db.Queryable<City>().Where(e => !string.IsNullOrEmpty(e.TimezoneId)).ToListAsync();
                await _IEasyCachingProvider.SetAsync<List<City>>(cacheKey, list, TimeSpan.FromHours(24));
                return list;
            }
        }

        protected async Task<List<Country>> GetCountries()
        {
            var cacheKey = "timezone_countries";
            var res = await _IEasyCachingProvider.GetAsync<List<Country>>(cacheKey);
            if (res.HasValue)
            {
                return res.Value;
            }
            else
            {
                var list = await dbContext.Db.Queryable<Country>().Where(e => !string.IsNullOrEmpty(e.TimezoneId)).ToListAsync();
                await _IEasyCachingProvider.SetAsync<List<Country>>(cacheKey, list, TimeSpan.FromHours(24));
                return list;
            }
        }
    }
}
